home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / Players / DDrawXCL / ddrawxcl.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  26.9 KB  |  872 lines

  1. //------------------------------------------------------------------------------
  2. // File: DDrawXcl.cpp
  3. //
  4. // Desc: DirectShow sample code - DDraw Exclusive Mode Video Playback 
  5. //       test/sample application.
  6. //
  7. // Copyright (c) 1993-2001 Microsoft Corporation.  All rights reserved.
  8. //------------------------------------------------------------------------------
  9.  
  10.  
  11. #include <streams.h>
  12. #include <windows.h>
  13. #include <commdlg.h>
  14.  
  15. #include <DVDEvCod.h>
  16. #include "VidPlay.h"
  17. #include "DDrawObj.h"
  18. #include "DDrawXcl.h"
  19.  
  20. //
  21. // WinMain(): Entry point to our sample app.
  22. //
  23. int APIENTRY WinMain(HINSTANCE hInstance,
  24.                      HINSTANCE hPrevInstance,
  25.                      LPSTR     lpCmdLine,
  26.                      int       nCmdShow)
  27. {
  28.     MSG      msg ;
  29.     HACCEL   hAccelTable ;
  30.     
  31.     DbgInitialise(hInstance) ;
  32.     
  33.     CoInitialize(NULL) ;
  34.     
  35.     ghInstance = hInstance ;
  36.     LoadString(ghInstance, IDS_APP_TITLE, gszAppTitle,  100) ;
  37.     LoadString(ghInstance, IDS_APP_NAME,  gszAppName,   10) ;
  38.     
  39.     if (! InitApplication() ) 
  40.     {
  41.         DbgTerminate() ;
  42.         return FALSE ;
  43.     } 
  44.     
  45.     if (! InitInstance(nCmdShow) ) 
  46.     {
  47.         DbgTerminate() ;
  48.         return FALSE ;
  49.     } 
  50.     
  51.     hAccelTable = LoadAccelerators(hInstance, gszAppName) ;
  52.     
  53.     //
  54.     // Create a DDraw object and init it
  55.     //
  56.     gpDDrawObj = new CDDrawObject(ghWndApp) ;
  57.     if (NULL == gpDDrawObj)
  58.     {
  59.         DbgTerminate() ;
  60.         return FALSE ;
  61.     }
  62.     
  63.     gpPlayer = NULL ;           // Init Video playback object pointer to NULL
  64.     geVideoType = Unspecified ; // no video type specified on start up
  65.     gbAppActive = TRUE ;        // app is activated on start up
  66.     
  67.     // Main message loop:
  68.     while (GetMessage(&msg, NULL, 0, 0))
  69.     {
  70.         if (! TranslateAccelerator(msg.hwnd, hAccelTable, &msg) )
  71.         {
  72.             TranslateMessage(&msg) ;
  73.             DispatchMessage(&msg) ;
  74.         }
  75.     }
  76.     
  77.     //
  78.     // Release the video playback object, if any
  79.     //
  80.     if (gpPlayer)
  81.         delete gpPlayer ;
  82.     
  83.     //
  84.     // Release DDraw now
  85.     //
  86.     delete gpDDrawObj ;
  87.     
  88.     CoUninitialize() ;
  89.     
  90.     DbgTerminate() ;
  91.     return (int) (msg.wParam) ;
  92. }
  93.  
  94.  
  95. //
  96. // InitApplication(): Registers the class if no other instance of this app is 
  97. // already running.
  98. //
  99. BOOL InitApplication(void)
  100. {
  101.     DbgLog((LOG_TRACE, 5, TEXT("App's InitApplication() entered"))) ;
  102.     
  103.     WNDCLASSEX  wc ;
  104.     
  105.     // Win32 will always set hPrevInstance to NULL, so lets check
  106.     // things a little closer. This is because we only want a single
  107.     // version of this app to run at a time
  108.     ghWndApp = FindWindow (gszAppName, gszAppTitle) ;
  109.     if (ghWndApp) {
  110.         // We found another version of ourself. Lets defer to it:
  111.         if (IsIconic(ghWndApp)) {
  112.             ShowWindow(ghWndApp, SW_RESTORE);
  113.         }
  114.         SetForegroundWindow(ghWndApp);
  115.         
  116.         // If this app actually had any functionality, we would
  117.         // also want to communicate any action that our 'twin'
  118.         // should now perform based on how the user tried to
  119.         // execute us.
  120.         return FALSE;
  121.     }
  122.     
  123.     // Register the app main window class
  124.     wc.cbSize        = sizeof(wc) ;
  125.     wc.style         = CS_HREDRAW | CS_VREDRAW ;
  126.     wc.lpfnWndProc   = (WNDPROC) WndProc ;
  127.     wc.cbClsExtra    = 0 ;
  128.     wc.cbWndExtra    = 0 ;
  129.     wc.hInstance     = ghInstance ;
  130.     wc.hIcon         = LoadIcon(ghInstance, gszAppName) ;
  131.     wc.hCursor       = LoadCursor(NULL, IDC_ARROW) ;
  132.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1) ;
  133.     wc.lpszMenuName  = gszAppName ;
  134.     wc.lpszClassName = gszAppName ;
  135.     wc.hIconSm       = NULL ;
  136.     if (0 == RegisterClassEx(&wc))
  137.     {
  138.         DbgLog((LOG_ERROR, 0, 
  139.             TEXT("ERROR: RegisterClassEx() for app class failed (Error %ld)"), 
  140.             GetLastError())) ;
  141.         return FALSE ;
  142.     }
  143.     
  144.     return TRUE ;
  145. }
  146.  
  147.  
  148. //
  149. // InitInstance(): Starts this instance of the app (creates the window).
  150. //
  151. BOOL InitInstance(int nCmdShow)
  152. {
  153.     DbgLog((LOG_TRACE, 5, TEXT("App's InitInstance() entered"))) ;
  154.     
  155.     ghWndApp = CreateWindowEx(0, gszAppName, gszAppTitle, 
  156.         WS_OVERLAPPEDWINDOW,
  157.         0, 0, 
  158.         DEFAULT_WIDTH, DEFAULT_HEIGHT,
  159.         NULL, NULL, ghInstance, NULL);
  160.  
  161.     if (! ghWndApp ) {
  162.         return FALSE ;
  163.     }
  164.     
  165.     ShowWindow(ghWndApp, nCmdShow);
  166.     UpdateWindow(ghWndApp) ;
  167.     
  168.     return TRUE ;
  169. }
  170.  
  171.  
  172. //
  173. // MenuProc(): Handles menu choices picked by the user.
  174. //
  175. LRESULT CALLBACK MenuProc(HWND hWnd, WPARAM wParam, LPARAM lParam)
  176. {
  177.     HMENU hMenu = GetMenu(hWnd) ;
  178.     int   wmId    = LOWORD(wParam);
  179.     // int   wmEvent = HIWORD(wParam);
  180.     
  181.     //Parse the menu selections:
  182.     switch (wmId) {
  183.         
  184.     case IDM_SELECTDVD:
  185.         if (FileSelect(hWnd, DVD))  // selection changed
  186.         {
  187.             CheckMenuItem(hMenu, IDM_SELECTDVD,  MF_CHECKED) ;
  188.             CheckMenuItem(hMenu, IDM_SELECTFILE, MF_UNCHECKED) ;
  189.         }
  190.         break;
  191.         
  192.     case IDM_SELECTFILE:
  193.         if (FileSelect(hWnd, File))  // selection changed
  194.         {
  195.             CheckMenuItem(hMenu, IDM_SELECTDVD,  MF_UNCHECKED) ;
  196.             CheckMenuItem(hMenu, IDM_SELECTFILE, MF_CHECKED) ;
  197.         }
  198.         break;
  199.         
  200.     case IDM_ABOUT:
  201.         DialogBox(ghInstance, TEXT("AboutBox"), ghWndApp, (DLGPROC) About);
  202.         break;
  203.         
  204.     case IDM_EXIT:
  205.         DestroyWindow(ghWndApp);
  206.         break;
  207.         
  208.     case IDM_STARTPLAY:
  209.         if (StartPlay(hWnd))  // playback started successfully
  210.             gbAppActive = TRUE ;   // if we are playing, we must be active
  211.         else                  // playback failed due to some reason
  212.             MessageBox(hWnd, 
  213.                 STR_EXCLUSIVE_MODE_FAILURE,
  214.                 TEXT("Error"), MB_OK | MB_ICONINFORMATION) ;
  215.         break;
  216.         
  217.     default:
  218.         break ;
  219.     }
  220.     
  221.     return 0 ;
  222. }
  223.  
  224.  
  225. //
  226. // FileSelect(): Lets the user specify the file to play.
  227. //
  228. BOOL FileSelect(HWND hWnd, VIDEO_TYPE eType)
  229. {
  230.     DbgLog((LOG_TRACE, 5, TEXT("App's FileSelect(%s) entered"),
  231.         DVD == eType ? "DVD" : "File")) ;
  232.     
  233.     OPENFILENAME  ofn ;
  234.     TCHAR         achFileName[MAX_PATH] ;
  235.     
  236.     // Init the filename buffer with either a filename or *.ifo
  237.     if (DVD == eType)
  238.         lstrcpy(achFileName, TEXT("*.ifo")) ;
  239.     else
  240.         lstrcpy(achFileName, TEXT("*.avi")) ;
  241.     
  242.     ZeroMemory(&ofn, sizeof(OPENFILENAME)) ;
  243.     ofn.lStructSize = sizeof(OPENFILENAME) ;
  244.     ofn.hwndOwner = hWnd ;
  245.     if (DVD == eType)
  246.     {
  247.         ofn.lpstrTitle = TEXT("Select DVD-Video Volume\0") ;
  248.         ofn.lpstrFilter = TEXT("IFO Files\0*.ifo\0All Files\0*.*\0\0") ;
  249.     }
  250.     else
  251.     {
  252.         ofn.lpstrTitle = TEXT("Select Video file\0") ;
  253.         ofn.lpstrFilter = TEXT("AVI Files\0*.avi\0MPEG Files\0*.mpg\0All Files\0*.*\0\0") ;
  254.     }
  255.     ofn.nFilterIndex = 1 ;
  256.     ofn.lpstrFile = achFileName ;
  257.     ofn.nMaxFile = sizeof(achFileName) ;
  258.     ofn.lpstrFileTitle = NULL ;
  259.     ofn.nMaxFileTitle = 0 ;
  260.     ofn.lpstrInitialDir = NULL ;
  261.     ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY ;
  262.     
  263.     if (GetOpenFileName(&ofn)) // user specified a file
  264.     {
  265.         if (! CreatePlayer(eType)  ||  // creating player failed!!
  266.             NULL == gpPlayer )         // how!?!
  267.         {
  268.             DbgLog((LOG_ERROR, 0, TEXT("ERROR: Couldn't create %s player"),
  269.                 DVD == eType ? "DVD" : "File")) ;
  270.             return FALSE ;
  271.         }
  272.         gpPlayer->SetFileName(achFileName) ;
  273.         return TRUE ;  // user specified file name
  274.     }
  275.     
  276.     // Either failed or user hit Esc.
  277.     DbgLog((LOG_TRACE, 3, TEXT("GetOpenFileName() cancelled/failed (Error %lu)"), 
  278.             CommDlgExtendedError())) ;
  279.     return FALSE ; // DVD-Video volume not changed
  280. }
  281.  
  282.  
  283. BOOL IsVideoTypeKnown(void)
  284. {
  285.     return (Unspecified != geVideoType) ;
  286. }
  287.  
  288.  
  289. VIDEO_TYPE GetVideoType(void)
  290. {
  291.     return geVideoType ;
  292. }
  293.  
  294.  
  295. BOOL CreatePlayer(VIDEO_TYPE eType)
  296. {
  297.     if (geVideoType == eType)  // same type as before
  298.     {
  299.         if (gpPlayer)          // we have already have the player
  300.             return TRUE     ;  // we'll use the same one; everything is OK
  301.     }
  302.     else                       // video type has changed
  303.     {
  304.         if (gpPlayer)          // we created a player before...
  305.         {
  306.             delete gpPlayer ;  // release it now
  307.             gpPlayer = NULL ;
  308.         }
  309.     }
  310.     
  311.     // If we are here, we need to create a new player of the specified type
  312.     if (DVD == eType)
  313.         gpPlayer = new CDVDPlayer ;
  314.     else if (File == eType)
  315.         gpPlayer = new CFilePlayer ;
  316.     else  // what then??
  317.     {
  318.         ASSERT(FALSE) ;
  319.         return NULL ;
  320.     }
  321.     
  322.     geVideoType = eType ;   // this our current video type
  323.     
  324.     return TRUE ;
  325. }
  326.  
  327.  
  328. //
  329. // OnEndOfPlayback(): Releases everything on end of playback (but checks to
  330. // avoid doing it too many times) as it "may be" called a little more than
  331. // we would like to.
  332. //
  333. void OnEndOfPlayback(HWND hWndApp)
  334. {
  335.     DbgLog((LOG_TRACE, 5, TEXT("App's OnEndOfPlayback() entered"))) ;
  336.     
  337.     if (0 != guTimerID)  // if any timer is still set
  338.     {
  339.         BOOL bRes = KillTimer(hWndApp, TIMER_ID) ;  // don't need that timer anymore
  340.         ASSERT(bRes) ;      bRes = bRes;  // Suppress C4189 warning
  341.         guTimerID = 0 ;  // timer released
  342.     }
  343.     
  344.     if (gpPlayer && gpPlayer->IsGraphReady())
  345.     {
  346.         DbgLog((LOG_TRACE, 5, TEXT("Turn off color keying before stopping the graph"))) ;
  347.         gpDDrawObj->SetOverlayState(FALSE) ;   // don't paint color key in video's position
  348. #ifndef NOFLIP
  349.         gpDDrawObj->UpdateAndFlipSurfaces() ;  // flip the surface so that video doesn't show anymore
  350. #endif // NOFLIP
  351.         
  352.         gpPlayer->Stop() ;
  353.         
  354.         // Remove the overlay callback interface for OverlayMixer
  355.         HRESULT hr = gpPlayer->SetOverlayCallback(NULL) ;
  356.         ASSERT(SUCCEEDED(hr)) ;     hr = hr;  // Suppress C4189 warning
  357.         
  358.         gpPlayer->ClearGraph() ;
  359.     }
  360.     
  361.     if (gpDDrawObj->IsInExclusiveMode())
  362.     {
  363.         gpDDrawObj->StopExclusiveMode(hWndApp) ;
  364.  
  365.         // Resize main window to default size
  366.         SetWindowPos(ghWndApp, HWND_TOP, 0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT, 0);
  367.     }
  368. }
  369.  
  370.  
  371. //
  372. // WndPorc(): Message handles for our sample app.
  373. //
  374. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  375. {
  376.     static HDC          hDC ;
  377.     static PAINTSTRUCT  ps ;
  378.     
  379.     switch (message) {
  380.         
  381.     case WM_ACTIVATEAPP:
  382.         //
  383.         // NOTE: We don't try to recover playback on switching back into focus.
  384.         //
  385.         gbAppActive = (BOOL) wParam ;
  386.         if (! gbAppActive )  // losing activation
  387.         {
  388.             DbgLog((LOG_TRACE, 2, TEXT("Got a WM_ACTIVATEAPP message with Active = %s"),
  389.                 gbAppActive ? "TRUE" : "FALSE")) ;
  390.             OnEndOfPlayback(hWnd) ;  // should stop playback now
  391.         }
  392.         break ;
  393.         
  394.     case WM_TIMER:
  395.         DbgLog((LOG_TRACE, 4, TEXT("Got a WM_TIMER message with ID = %ld (Active = %s)"), 
  396.             wParam, gbAppActive ? "T" : "F")) ;
  397.         if ( TIMER_ID != wParam  ||      // this is not the timer we have set  or
  398.             ! gbAppActive ||             // the app isn't active anymore  or
  399.             ! gpPlayer->IsGraphReady() ) // the graph is not currently ready
  400.             // (...but we should have turned timer off then anyway!!)
  401.             break ;     // don't do anything
  402.  
  403.         //
  404.         // We could do some status update here that could be used by the
  405.         // UpdateAndFlipSurfaces() call below.
  406.         //
  407. #ifndef NOFLIP
  408.         gpDDrawObj->UpdateAndFlipSurfaces() ;
  409. #endif // NOFLIP
  410.         
  411.         break;
  412.         
  413.     case WM_COMMAND:
  414.         DbgLog((LOG_TRACE, 4, TEXT("Got a WM_COMMAND message with wParam = %ld"), wParam)) ;
  415.         MenuProc(hWnd, wParam, lParam) ;
  416.         break;
  417.         
  418.     case WM_PLAY_EVENT:
  419.         DbgLog((LOG_TRACE, 4, TEXT("Got a WM_PLAY_EVENT message with wParam = %ld"), wParam)) ;
  420.         if (1 == OnPlaybackEvent(hWnd, wParam, lParam))  // playback ended
  421.             OnEndOfPlayback(hWnd) ;     // do the necessary things
  422.         break ;
  423.         
  424.     case WM_SIZE_CHANGE:
  425.         DbgLog((LOG_TRACE, 4, TEXT("Got a WM_SIZE_CHANGE message"))) ;
  426.         if (gpPlayer->IsGraphReady()) // ONLY if the graph is ready
  427.             SetVideoPosition(FALSE) ;
  428.         else
  429.             DbgLog((LOG_TRACE, 1, TEXT("WARNING: Got a WM_SIZE_CHANGE message after graph was released!!"))) ;
  430.         break ;
  431.         
  432.     case WM_KEYUP:
  433.         DbgLog((LOG_TRACE, 4, TEXT("Got a WM_KEYUP message with wParam = %ld"), wParam)) ;
  434.         KeyProc(hWnd, wParam, lParam) ;
  435.         break ;
  436.         
  437.     case WM_DESTROY:
  438.         OnEndOfPlayback(hWnd) ;  // must stop playback before quitting
  439.         PostQuitMessage(0);
  440.         break;
  441.         
  442.     default:
  443.         return (DefWindowProc(hWnd, message, wParam, lParam));
  444.     }
  445.     
  446.     return 0 ;
  447. }
  448.  
  449.  
  450. //
  451. // KeyProc(): Handles key presses to exit playback (on Esc) or move the ball
  452. // using arrow keys.
  453. //
  454. LRESULT CALLBACK KeyProc(HWND hWnd, WPARAM wParam, LPARAM lParam)
  455. {
  456.     DbgLog((LOG_TRACE, 5, TEXT("App's KeyProc() entered"))) ;
  457.     
  458.     switch (wParam)
  459.     {
  460.     case VK_ESCAPE:
  461.         OnEndOfPlayback(hWnd) ;
  462.         break ;
  463.         
  464.     case VK_UP:
  465.         gpDDrawObj->MoveBallPosition(0, -BALL_STEP) ;
  466.         break ;
  467.         
  468.     case VK_DOWN:
  469.         gpDDrawObj->MoveBallPosition(0, BALL_STEP) ;
  470.         break ;
  471.         
  472.     case VK_LEFT:
  473.         gpDDrawObj->MoveBallPosition(-BALL_STEP, 0) ;
  474.         break ;
  475.         
  476.     case VK_RIGHT:
  477.         gpDDrawObj->MoveBallPosition(BALL_STEP, 0) ;
  478.         break ;
  479.         
  480.     default:
  481.         break ;
  482.     }
  483.     
  484.     return 0 ;
  485. }
  486.  
  487.  
  488. //
  489. // About(): Dialog box code for the About box.
  490. //
  491. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
  492. {
  493.     DbgLog((LOG_TRACE, 5, TEXT("App's About() entered"))) ;
  494.     
  495.     switch (message) {
  496.         case WM_INITDIALOG:
  497.             return TRUE;
  498.         
  499.         case WM_COMMAND:
  500.             if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
  501.             {
  502.                 EndDialog(hDlg, TRUE);
  503.                 return TRUE;
  504.             }
  505.             break;
  506.         
  507.         default:
  508.             break ;
  509.     }
  510.     
  511.     return FALSE;
  512. }
  513.  
  514.  
  515. //
  516. // OnPlaybackEvent(): Handles playback events, specially completed/stopped-on-error.
  517. //
  518. // Returns 0 if playback is continuing.
  519. // Returns 1 if playback is over for any reason.
  520. //
  521. LRESULT OnPlaybackEvent(HWND hWnd, WPARAM wParam, LPARAM lParam)
  522. {
  523.     DbgLog((LOG_TRACE, 5,
  524.         TEXT("App's OnPlaybackEvent(0x%lx, 0x%lx) entered"),
  525.         wParam, lParam)) ;
  526.     
  527.     IMediaEventEx  *pME = (IMediaEventEx *) lParam ;
  528.     if (NULL == pME  || Playing != gpPlayer->GetState())
  529.     {
  530.         DbgLog((LOG_TRACE, 1, TEXT("Either pME = NULL or not playing anymore. Skip everything."))) ;
  531.         return 0 ;  // or 1 ??
  532.     }
  533.     
  534.     LONG     lEvent ;
  535.     LONG_PTR   lParam1, lParam2 ;
  536.     
  537.     //
  538.     //  Because the message mode for IMediaEvent may not be set before
  539.     //  we get the first event it's important to read all the events
  540.     //  pending when we get a window message to say there are events pending.
  541.     //  GetEvent() returns E_ABORT when no more event is left.
  542.     //
  543.     while (SUCCEEDED(pME->GetEvent(&lEvent, &lParam1, &lParam2, 0))) // no wait
  544.     {
  545.         switch (lEvent)
  546.         {
  547.         //
  548.         // First the DVD related events
  549.         //
  550.         case EC_DVD_STILL_ON:
  551.             DbgLog((LOG_TRACE, 5, TEXT("Playback Event: EC_DVD_STILL_ON"))) ;
  552.             break ;
  553.             
  554.         case EC_DVD_STILL_OFF:
  555.             DbgLog((LOG_TRACE, 5, TEXT("Playback Event: EC_DVD_STILL_OFF"))) ;
  556.             break ;
  557.             
  558.         case EC_DVD_DOMAIN_CHANGE:
  559.             DbgLog((LOG_TRACE, 5, TEXT("Playback Event: EC_DVD_DOMAIN_CHANGE, %ld"), lParam1)) ;
  560.             switch (lParam1)
  561.             {
  562.                 case DVD_DOMAIN_FirstPlay:  // = 1
  563.                 case DVD_DOMAIN_Stop:       // = 5
  564.                     break ;
  565.                 
  566.                 case DVD_DOMAIN_VideoManagerMenu:  // = 2
  567.                 case DVD_DOMAIN_VideoTitleSetMenu: // = 3
  568.                     // Inform the app to update the menu option to show "Resume" now
  569.                     break ;
  570.                 
  571.                 case DVD_DOMAIN_Title:      // = 4
  572.                     // Inform the app to update the menu option to show "Menu" again
  573.                     break ;
  574.                 
  575.                 default: // hmmmm...
  576.                     break ;
  577.             }
  578.             break ;
  579.             
  580.             case EC_DVD_BUTTON_CHANGE:
  581.                 DbgLog((LOG_TRACE, 5, TEXT("DVD Event: Button Changed to %d out of %d"),
  582.                     lParam2, lParam1));
  583.                 break;
  584.                 
  585.             case EC_DVD_TITLE_CHANGE:
  586.                 DbgLog((LOG_TRACE, 5, TEXT("Playback Event: EC_DVD_TITLE_CHANGE"))) ;
  587.                 break ;
  588.                 
  589.             case EC_DVD_CHAPTER_START:
  590.                 DbgLog((LOG_TRACE, 5, TEXT("Playback Event: EC_DVD_CHAPTER_START"))) ;
  591.                 break ;
  592.                 
  593.             case EC_DVD_CURRENT_TIME:
  594.                 DbgLog((LOG_TRACE, 5, TEXT("Playback Event: EC_DVD_CURRENT_TIME"))) ;
  595.                 break ;
  596.                 
  597.             //
  598.             // Then the general DirectShow related events
  599.             //
  600.             case EC_COMPLETE:
  601.                 DbgLog((LOG_TRACE, 5, TEXT("Playback Event: Playback complete"))) ;
  602.                 MessageBeep(MB_OK) ; // just to inform that the playback is over
  603.                 
  604.                 // Remember to free the event params
  605.                 pME->FreeEventParams(lEvent, lParam1, lParam2) ;
  606.                 
  607.                 // We don't do the release part here. That will be done in WndProc()
  608.                 // after return from this function.
  609.                 return 1 ;  // playback over
  610.                 
  611.             case EC_USERABORT:
  612.             case EC_ERRORABORT:
  613.                 DbgLog((LOG_TRACE, 5, TEXT("Playback Event: 0x%lx"), lEvent)) ;
  614.                 MessageBeep(MB_ICONEXCLAMATION) ; // to inform that the playback has errored out
  615.                 
  616.                 // Remember to free the event params
  617.                 pME->FreeEventParams(lEvent, lParam1, lParam2) ;
  618.                 
  619.                 // We don't do the release part here. That will be done in WndProc()
  620.                 // after return from this function.
  621.                 return 1 ;  // playback over
  622.                 
  623.             default:
  624.                 DbgLog((LOG_TRACE, 5, TEXT("Ignored unknown playback event: 0x%lx"), lEvent)) ;
  625.                 break ;
  626.         }
  627.         
  628.         //
  629.         // Remember to free the event params
  630.         //
  631.         pME->FreeEventParams(lEvent, lParam1, lParam2) ;
  632.         
  633.     }  // end of while (GetEvent()) loop
  634.     
  635.     return 0 ;
  636. }
  637.  
  638.  
  639. typedef enum _AM_TRANSFORM
  640. {
  641.     AM_SHRINK,
  642.     AM_STRETCH
  643. } AM_TRANSFORM ;
  644.  
  645. void TransformRect(RECT *prRect, double dPictAspectRatio, AM_TRANSFORM transform)
  646. {
  647.     double dWidth, dHeight, dNewWidth, dNewHeight ;
  648.     
  649.     double dResolutionRatio = 0.0, dTransformRatio = 0.0 ;
  650.     
  651.     ASSERT(transform == AM_SHRINK || transform == AM_STRETCH) ;
  652.     
  653.     dNewWidth = dWidth = prRect->right - prRect->left ;
  654.     dNewHeight = dHeight = prRect->bottom - prRect->top ;
  655.     
  656.     dResolutionRatio = dWidth / dHeight ;
  657.     dTransformRatio = dPictAspectRatio / dResolutionRatio ;
  658.     
  659.     // shrinks one dimension to maintain the coorect aspect ratio
  660.     if (transform == AM_SHRINK)
  661.     {
  662.         if (dTransformRatio > 1.0)
  663.         {
  664.             dNewHeight = dNewHeight / dTransformRatio ;
  665.         }
  666.         else if (dTransformRatio < 1.0)
  667.         {
  668.             dNewWidth = dNewWidth * dTransformRatio ;
  669.         }
  670.     }
  671.     // stretches one dimension to maintain the coorect aspect ratio
  672.     else if (transform == AM_STRETCH)
  673.     {
  674.         if (dTransformRatio > 1.0)
  675.         {
  676.             dNewWidth = dNewWidth * dTransformRatio ;
  677.         }
  678.         else if (dTransformRatio < 1.0)
  679.         {
  680.             dNewHeight = dNewHeight / dTransformRatio ;
  681.         }
  682.     }
  683.     
  684.     if (transform == AM_SHRINK)
  685.     {
  686.         ASSERT(dNewHeight <= dHeight) ;
  687.         ASSERT(dNewWidth <= dWidth) ;
  688.     }
  689.     else
  690.     {
  691.         ASSERT(dNewHeight >= dHeight) ;
  692.         ASSERT(dNewWidth >= dWidth) ;
  693.     }
  694.     
  695.     // cut or add equal portions to the changed dimension
  696.     
  697.     prRect->left += (LONG)(dWidth - dNewWidth)/2 ;
  698.     prRect->right = prRect->left + (LONG)dNewWidth ;
  699.     
  700.     prRect->top += (LONG)(dHeight - dNewHeight)/2 ;
  701.     prRect->bottom = prRect->top + (LONG)dNewHeight ;
  702. }
  703.  
  704.  
  705. //
  706. // SetVideoPosition(): Gets the original video size and positions it at the center.
  707. // 
  708. void SetVideoPosition(BOOL bSetBallPosition)
  709. {
  710.     DbgLog((LOG_TRACE, 5, TEXT("App's SetVideoPosition() entered"))) ;
  711.     
  712.     DWORD  dwVideoWidth, dwVideoHeight ;
  713.     DWORD  dwARX, dwARY ;
  714.     gpPlayer->GetNativeVideoData(&dwVideoWidth, &dwVideoHeight, &dwARX, &dwARY) ;
  715.     DbgLog((LOG_TRACE, 5, TEXT("Native video size: %lu x %lu, Aspect Ratio: %lu x %lu"), 
  716.         dwVideoWidth, dwVideoHeight, dwARX, dwARY)) ;
  717.     
  718.     // Update output size to make it aspect ratio corrected
  719.     RECT  rectCorrected ;
  720.     SetRect(&rectCorrected, 0, 0, dwVideoWidth, dwVideoHeight) ;
  721.     TransformRect(&rectCorrected, (double)dwARX / (double)dwARY, AM_STRETCH) ;
  722.     DbgLog((LOG_TRACE, 5, TEXT("Updated video size: %ld x %ld"), 
  723.         RECTWIDTH(rectCorrected), RECTHEIGHT(rectCorrected))) ;
  724.     
  725.     RECT   ScrnRect ;
  726.     gpDDrawObj->GetScreenRect(&ScrnRect) ;
  727.     DbgLog((LOG_TRACE, 5, TEXT("Screen size is %ld x %ld"), 
  728.             RECTWIDTH(ScrnRect), RECTHEIGHT(ScrnRect))) ;
  729.     
  730.     DWORD  dwVideoTop ;
  731.     DWORD  dwVideoLeft ;
  732.     if (RECTWIDTH(rectCorrected)  <= RECTWIDTH(ScrnRect)  &&  // video width less than screen
  733.         RECTHEIGHT(rectCorrected) <= RECTHEIGHT(ScrnRect))    // video height less than screen
  734.     {
  735.         dwVideoLeft = (RECTWIDTH(ScrnRect)  - RECTWIDTH(rectCorrected)) / 2 ;
  736.         dwVideoTop  = (RECTHEIGHT(ScrnRect) - RECTHEIGHT(rectCorrected)) / 2 ;
  737.     }
  738.     else                            // video width more than screen
  739.     {
  740.         rectCorrected = ScrnRect ;
  741.         TransformRect(&rectCorrected, (double)dwARX / (double)dwARY, AM_SHRINK) ;
  742.         dwVideoLeft = rectCorrected.left ;
  743.         dwVideoTop  = rectCorrected.top ;
  744.     }
  745.     gpDDrawObj->SetVideoPosition(dwVideoLeft, dwVideoTop, 
  746.         RECTWIDTH(rectCorrected), RECTHEIGHT(rectCorrected)) ;
  747.     if (bSetBallPosition)           // if ball position should be (re)set
  748.         gpDDrawObj->SetBallPosition(dwVideoLeft, dwVideoTop, 
  749.                                 RECTWIDTH(rectCorrected), RECTHEIGHT(rectCorrected)) ;
  750.     else                            // don't reset the ball position, just...
  751.         gpDDrawObj->MoveBallPosition(0, 0) ;  // ... make sure it's in the new video area
  752.     gpPlayer->SetVideoPosition(dwVideoLeft, dwVideoTop, 
  753.                                 RECTWIDTH(rectCorrected), RECTHEIGHT(rectCorrected)) ;
  754.     DbgLog((LOG_TRACE, 5, TEXT("Video is %ld x %ld at (%ld x %ld)"), 
  755.             RECTWIDTH(rectCorrected), RECTHEIGHT(rectCorrected), dwVideoLeft, dwVideoTop)) ;
  756. }
  757.  
  758.  
  759. //
  760. // StartPlay(): Switches to fulscreen exclusive mode, sets up for selected media
  761. // playback, gets the video size and positions video at the center, starts playing
  762. // and sets a timer to tell WndProc() every 1/10 second.
  763. //
  764. LRESULT StartPlay(HWND hWndApp)
  765. {
  766.     HRESULT  hr ;
  767.     
  768.     DbgLog((LOG_TRACE, 5, TEXT("App's StartPlay() entered"))) ;
  769.     
  770.     if (! IsVideoTypeKnown() )
  771.     {
  772.         MessageBox(hWndApp, 
  773.             TEXT("No playback option (DVD/File) has been specified through the menu option yet.\nCan't run test."), 
  774.             TEXT("Sorry"), MB_OK | MB_ICONINFORMATION) ;
  775.         return 0 ;
  776.     }
  777.     
  778.     //
  779.     // Make sure that DShow components are installed etc so that
  780.     // the graph building can be init-ed
  781.     //
  782.     if (! gpPlayer->Initialize() )
  783.     {
  784.         MessageBox(hWndApp, 
  785.             TEXT("DShow components couldn't be initialized.\n\nCan't run test.\nPlease check DxMedia installation"), 
  786.             TEXT("Sorry"), MB_OK | MB_ICONSTOP) ;
  787.         return 0 ;
  788.     }
  789.     
  790.     //
  791.     // Go into fullscreen exclusive mode
  792.     //
  793.     hr = gpDDrawObj->StartExclusiveMode(hWndApp) ;
  794.     if (FAILED(hr))  // error message shown by the above method
  795.     {
  796.         gpPlayer->ClearGraph() ;
  797.         return 0 ;
  798.     }
  799.     
  800.     //
  801.     // Build video playback graph
  802.     //
  803.     hr = gpPlayer->BuildGraph(hWndApp, gpDDrawObj->GetDDObject(), gpDDrawObj->GetDDPrimary()) ;
  804.     if (FAILED(hr))
  805.     {
  806.         gpPlayer->ClearGraph() ;
  807.         gpDDrawObj->StopExclusiveMode(hWndApp) ;  // get out of exclusive mode
  808.         return 0 ;
  809.     }
  810.     
  811.     //
  812.     // Specify the overlay callback interface for OverlayMixer to notify us
  813.     //
  814.     hr = gpPlayer->SetOverlayCallback(gpDDrawObj->GetCallbackInterface()) ;
  815.     ASSERT(SUCCEEDED(hr)) ;
  816.     
  817.     //
  818.     // Pause the video playback graph to get it ready to play
  819.     //
  820.     BOOL bSuccess = gpPlayer->Pause() ;
  821.     if (!bSuccess)
  822.     {
  823.         gpPlayer->SetOverlayCallback(NULL) ;      // first remove overlay callback
  824.         gpPlayer->ClearGraph() ;                  // then remove graph
  825.         gpDDrawObj->StopExclusiveMode(hWndApp) ;  // then get out of exclusive mode
  826.         return 0 ;
  827.     }
  828.     
  829.     //
  830.     // Get the color key info from the Player object and pass it to the DDraw object
  831.     //
  832.     DWORD   dwVideoColorKey ;
  833.     gpPlayer->GetColorKey(&dwVideoColorKey) ;
  834.     gpDDrawObj->SetColorKey(dwVideoColorKey) ;
  835.     
  836.     //
  837.     // Get the video width and height, center it and pass the coordinates to 
  838.     // the player and the DDraw object
  839.     //
  840.     SetVideoPosition(TRUE) ;
  841.     
  842.     //
  843.     // Create the first screen on back buffer and then flip
  844.     //
  845. #ifndef NOFLIP
  846.     gpDDrawObj->UpdateAndFlipSurfaces() ;
  847. #endif // NOFLIP
  848.     
  849.     //
  850.     // Play video now...
  851.     //
  852.     if (! gpPlayer->Play() )
  853.     {
  854.         gpPlayer->SetOverlayCallback(NULL) ;      // first remove overlay callback
  855.         gpPlayer->ClearGraph() ;                  // then remove graph
  856.         gpDDrawObj->StopExclusiveMode(hWndApp) ;  // then get out of exclusive mode
  857.         return 0 ;
  858.     }
  859.     
  860.     //
  861.     // Now set a timer based on which we'll update the buffers and flip
  862.     //
  863.     guTimerID = (UINT) SetTimer(hWndApp, TIMER_ID, TIMER_RATE, NULL) ;
  864.     ASSERT(0 != guTimerID) ;
  865.     
  866.     // We are done with starting the playback.  WndProc will stop the playback on
  867.     // playback event messages or user hitting Esc key as well the timer based
  868.     // actions will be taken in WM_TIMER handler there.
  869.     
  870.     return 1 ;
  871. }
  872.